perm filename MEMO64.PUB[HAL,HE] blob sn#117126 filedate 1974-08-28 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00011 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	.vhl: NEWSEC VERY HIGH LEVEL LANGUAGE CAPABILITIES
C00009 00003	.NEWSS MACRO OPERATIONS AS A α`HIGH LEVEL LANGUAGEα',MACRO-OPERATIONS
C00011 00004	.NEWSS MORE POWERFUL PRIMITIVES -- AN OVERVIEW,HIGH-LEVEL PRIMITIVES
C00017 00005	.chl:NEWSS CALLING HIGH LEVEL PRIMITIVES
C00024 00006	.NEWSS WORLD MODELLING OVERVIEW
C00028 00007	.var: NEWSS INFORMATION ABOUT VARIABLES
C00036 00008	.obd: NEWSS OBJECT DESCRIPTION
C00039 00009	.unfill
C00046 00010	.newsss ASSEMBLIES
C00049 00011	.NEWSS EXAMPLE: WATERPUMP ASSEMBLY PROGRAM,WATERPUMP ASSEMBLY
C00059 ENDMK
C⊗;
.vhl: NEWSEC VERY HIGH LEVEL LANGUAGE CAPABILITIES
.NEWSS INTRODUCTION 

To date, manipulator control languages have been very explicit,
with the user giving detailed specifications of what
motions are to be made,  what sensors are to be tested, etc. To some
extent this is also true of HAL.  One can conceive of programming
complicated assemblies using only MOVE and OPERATE statements, 
ON monitors, and the like.
In practice, however,  such an approach has
many disadvantages for users,
who frequently don't care about
all the details needed to produce a program at the manipulator level.
For instance,  an assembly engineer who wants to put an engine
together might typically want to write something like:
.unfill
	:
	FIT enginehead ONTO engineblock
		WITH ALIGNMENT stud_x IN headhole_x,
			       stud_y IN headhole_y;
	INSERT bolt1 IN headhole1
		USING TOOL driver
		WITH TORQUE 10;
	INSERT bolt2 IN headhole2
		USING TOOL driver
		WITH TORQUE 10;
	INSERT drainplug IN sidehole;
	:

.refill
and allow the system to fill in the details, rather than coding up
all the motions herself.

This chapter gives an overview of those parts of HAL that allow the
user to specify tasks at somewhat more convenient levels of
abstraction than provided by the manipulation control statements
alone.  Here, we are concerned with a "semi-automatic" programming
system that can make a number of the specific decisions required to
turn an "high level" task description into a running program and
which can ask for (and accept) help for those details that it cannot
determine for itself.

The range of decisions that the system may have to make is quite
broad, ranging from very local matters, such as the number of
trajectories which must be planned to ensure correct performance of all
cases of some motion request, to rather global decisions, such as
how an object should be grasped, how an object orientation
is to be determined, or what should be the relative order for
executing several related subtasks.

Some of these decisions are essentially domain-independent.  For
example, the system decides how many different arm trajectories it
must plan for a given MOVE statement by examining its model of the
locus of the destination frame (see {ssref var}), without much regard for the
"meaning" of the frame variable.  Other decisions may require a
significant degree of specialized knowledge about the task domain.
For instance, the INSERT primitive in our example would need to know
how a nutdriver is used to grasp a bolt, what effects (if any) the
shape of the bolt tip or hole chamfer has on choice of search
method, what constraints are imposed on workpiece positioning, and
much more. One very important constraint on the output program is
that it be consistent, in the sense that code generated to accomplish
one subtask should not be inconsistent with (and, indeed, should
facilitate) the accomplishment of other subtasks.  This necessity, in
turn, frequently generates further requirements for specialized
knowledge about the requirements and effects of the primitives being
provided.  

We have chosen small scale industrial automation as a good domain
for investigating the issues involved in incorporating 
such specialized knowledge into HAL, and this discussion is
oriented accordingly.
However, many of the mechanisms underlying the various language features 
discussed here are fairly general; an expert system
for some other manipulatory domain could be organized along the same
lines and,  indeed, could use at least some of the same primitives.
The ease of such adaptation would depend, of course, on the closeness
of the domains and on the particular primitives involved.


.NEWSS MACRO OPERATIONS AS A α`HIGH LEVEL LANGUAGEα',MACRO-OPERATIONS

One obvious partial solution is to combine commonly
occuring code sequences into "macro operations",  and then allow the
user to specify tasks in terms of those operations.  HAL includes
sophisticated macro, defined routine, and conditional compilation
facilities (see {ssref ctc} and {ssref lib}) for this purpose.
Such library routines
have the advantage of being relatively easy for a person familiar
with HAL to write, and are generally at least partially
self-documenting.  Typically, a user wishing to know what a given
library routine does can find out merely by looking at a listing of
the routine, which will (of course) be written in a clear, well
structured style with many comments describing the more obscure
passages.  Generally, such libraries are most useful where there is
essentially only one way to do a given subtask,  all actions required
to do each subtask can be performed at one place in the output
program, and different subtasks are essentially independent. When
these conditions are not fully met, more powerful techniques are
needed.

.NEWSS MORE POWERFUL PRIMITIVES -- AN OVERVIEW,HIGH-LEVEL PRIMITIVES

Many domains are sufficiently complicated that macro expansion, even
when used with conditional compilation, is too limited.
In assembly, there may be a number of different ways to do some
particular task; which way is "right" depends very largely on what
other subtasks must be done. Similarly, it is frequently possible to
perform part of one subtask (or, at least, to gather useful
information) in the course of doing another one.
Such considerations are in general very
difficult to express within the paradigm of macro expansion.

In our introductory
example, for instance, the system must decide how the engine block is
to be oriented to facilitate putting on the head.  Furthermore, the
block alignment must be sufficiently well determined so that the the
aligning studs can find their way into the holes. One way to do this
might be to push the engine block up against a simple aligning jig
consisting of a low wall.  Other methods might include vision or
simply grasping key features of the block and reading the hand
coordinates.  Once the head is on, the system must insert the bolts
and drainplug.  The system would like to avoid moving the engine
block around more than it has to, since each move requires time and
may introduce uncertainties.  This means that it should choose a
block position that allows the arm to reach the bolt and drain holes.
If an aligning jig is in use, care should be taken to keep the side
hole free if possible, and so forth. Furthermore, an alignment technique that
visually locates the engine block head holes is apt to yield more
useful information for the insertion tasks than would some cruder,
but less expensive, test which may work just as well for the purpose
of mating the head.

A full discussion of the mechanisms used by the system to
transform a high level program into one that can actually run is
beyond the scope of this paper.  Briefly, HAL works by progressive
refinement of the user's program specifications, and uses process
instantiation and communication mechanisms to keep track of the
various subtasks and to ensure that all decisions are mutually
compatible.  Knowledge about assembly primitives is encoded into a
number of procedures inside the expander.  With each program
statement, the system associates a process instantiation of the
appropriate procedure (of course, the processes for low-level HAL
statements are fairly trivial). These processes are then arranged
into a prerequisite graph based initially on the user's specification
of what must be done before what (see {sssref tsk}).  A number of
other "bureaucrat" processes are created to work out compromises,
invent new service tasks, decide relative ordering, watch out for
obvious inefficiencies (such as putting down a tool and then picking
it right back up again), and so on.  As the plan becomes more detailed, and
as decisions are made about the order in which subtasks are to be
performed, successive copies of the program graph are generated.
(Additional information is stored both in the data base and in 
the internal state of the various subtask processes.)
The final phase is to run down the (linearized) graph, asking each
subtask process to generate the appropriate output code.

.chl:NEWSS CALLING HIGH LEVEL PRIMITIVES

The syntax for high level primitives is keyword-oriented and
resembles that for MOVE and OPERATE statements in the sense
that there is a main clause naming the operation, with possibly
a number of subordinate clauses giving further specifications 
as to what is to be done.  For example,
.unfill
	INSERT screw1 INTO hole1    α{main clauseα}
		USING TOOL nutdriver α{subordinate clauseα}
		WITH TORQUE = 10 α{subordinate clauseα}

.refill
For convenience and readability, a number of different forms
are acceptable.  For instance, the words "WITH" and "USING"
are interchangeable, and punctuation (like the "=", above) 
is frequently optional.  Some of the subordinate clauses
may themselves contain several elements, as in
.unfill
	FIT carburator ONTO engine_assembly_1
		WITH ALIGNMENT 
			carburator_hole1 OVER stud1,
			carburator_hole2 OVER stud2;
.refill
In such cases, a comma is used to delimit successive elements.

Initially, only a fairly small set of high level primitives is
being implemented, although some (like INSERT) may be quite flexible.
Even a few primitives, however, turn out to be sufficient for
many interesting tasks, and provide quite a rich environment for
investigation of how the various parts of the system interact.

Probably the most elaborate primitive is INSERT, which is generally
responsible for insertion of shafts and shaft-like objects 
(including screws) into 
holes.  The main clause is 
.unfill
	INSERT <shaft-specification> INTO <hole-specification>
.refill
where the <shaft-specification> should be either an object
of type shaft or one end of an object of type shaft.  In the
former case, the system will assume that the "bottom end"
of the specified shaft should be inserted  into the named
hole. (See {ssref obd})  Similarly, the <hole specification> may be 
either the name of a hole or of a bore, in which case the 
top end will be assumed.  HAL can learn a good part of 
exactly what it is being asked to do by looking at the 
object models.  For instance, if the shaft and bore are
both threaded and have the same diameter, then the system
will attempt to screw in the shaft properly.  Similarly, by
looking at the chamfer of the hole, the taper of the shaft,
and the region around the hole, the system can decide how 
much determination is required, what sort of search might
be applicable, etc.
Further specifications
may be included in subordinate clauses, such as the TOOL
and TORQUE clauses in our first example, or as in the
TWIST clause of
.unfill
	INSERT aligning_pin INTO guide_hole 
		WITH TWIST = 3
.refill
which says that the pin is to be given three turns counter-clockwise
as it is pushed into the guide hole.  Additional "advice" may also
be provided in the data base.  For instance, if there is a special
routine for grasping "type 1" screws with the nutdriver, there 
might be an assertion of the form:
.unfill
	FACT ( GRASPING_METHOD screwtype1 nutdriver routineid )
.refill
(This example rather oversimplifies the actual mechanism
used to describe this sort of thing; a fuller description, however,
is beyond the scope of this paper.)

.unfill
.bull
Another fairly elaborate primitive is 
	FIT <object1> ONTO <object2>
.refill
where <object1> is usually a subpart of assembly <object2>. (See
{ssref(obd)} for more details about assemblies.)  If
this is not the case, then the attachment location must be specified
by a clause of the form
.unfill
	AT <transform>
.refill
The most common modifying clause for this primitive is an
alignment specification, such as
.unfill
	WITH ALIGNMENT 
		<hole> OVER <shaft>,
		<obj 1 feature> MEETS <obj 2 feature>,
		<shaft> INTO <hole>,
		α{ et cetera α}

.maybreak
.bull
Other primitives include:

	SLIP <collar> OVER <shaft>  α{includes nut over threaded shaftα}

	PLACE <object> ON <surface>
		IN POSITION <stable position>  α{optionalα}

	GRASP <object> AT <trans or frame>

	TIGHTEN <bolt or nut> 
		WITH TORQUE <number>  α{this clause is requiredα}

	EXTRACT <shaft>  α{the inverse of INSERTα}

.refill
.NEWSS WORLD MODELLING OVERVIEW

The planning information required by the very high level primitives
is essentially a superset of that required for the basic
manipulation control statements; the same underlying mechanisms are
used, although sometimes in slightly different ways.  This includes
information about variable semantics, object shape and structure,
error estimates, and the purposes of programs, in addition to the
simple planning values and attachment structures used for low-level
trajectory planning.

The expander frequently needs to consider the effects of some hypothetical
action on a number of program steps.  Similarly, it is often necessary
to consider the effects of modifying some earlier decision or to
find a way to perform some preparatory action at an early point in
a program.  HAL handles provides for this sort of consideration by
the use of a simple "multi-world" data base.  Essentially, all fluent information
(such as planning values of variables, assertions, etc) is associated
with a set of "world" states for which it is true.  With every program
statement, HAL then associates an "input world", which contains the
planning model of the environment just before the statement gets executed,
and an "output world" which will reflect the expected effects of
the statement on the runtime world.  Normally, these two "worlds" 
can be the same when only low-level HAL statements are involved, since
such statements don't usually need to generate forward or backward references
to other planning states.  

Although multiple worlds are primarily intended for use by the expander,
a user can make explicit references to different worlds by using
.unfill
	IN <worldname>
.refill
in ASSERT and DENY statements and in the various COMPILE constructs.
The compile-time variables IWORLD and OWORLD always contain
HAL's internal names for the current input and output worlds, respectively.
For example,
.unfill
	ASSERT s=1;
	wα←IWORLD;
	:
	COMPILE IF (s=1) IN α#(w) THEN
		ASSERT s=2;
	Comment, now α#(s)=2;
.refill
Note:  IN acts syntactically like a very high priority boolean
binary operator, so that 
.unfill
	α#(a)=1 IN α#(w1) %7∨%* α#(b)=1 ∧ α#(a)=2 IN α#(w2) 
.bull
is equivalent to

	(α#(a)=1 IN α#(w1)) %7∨%* ((α#(b)=1 IN α#(IWORLD) ∧ (α#(a)=2 IN α#(w2))
.refill

.var: NEWSS INFORMATION ABOUT VARIABLES

The system must deal with a number of different sorts of information
about variables and variable values.  These include:
.begin narrow 8,8 

(1)%4 Metaphysical value.%* The metaphysical value of a variable is
that quantity which the variable is supposed to represent.
Traditionally, knowledge about the meaning of variables has been more
or less the exclusive province of the programmer.  For example, low level HAL
constructs don't usually know or care what some user-declared frame
variable really represents, although the system does understand a few
predeclared variables (e.g., YELLOW, which gives the location of
the yellow arm).  On the other hand, a statement like "fit the pumphead
onto the pump assembly" requires HAL to "know" what variables represent
object locations, mateing position, grasping positions, and so forth.

(2)%4 Runtime Value.%* This is the value that a given variable will
have at run time.  The compiler has a name for it, and must generate
code that references the corresponding memory location(s).

(3)%4 Locus information.%* Crudely put, the locus of a variable is
the set of possible runtime values for that variable. The term is
also used here to mean the compiler's estimate of the locus.  This
estimate may merely be the planning value, or it may include a 
region bounded by constraints.  These constraints may be expressed
explicitly, as mathematical relations involving degrees of freedom,
or implicitly, as semantic information like "the object is up
against the wall."

(4)%4 Determination information.%* The determination of a variable is
essentially a compile-time estimate of how accurately a runtime value
will reflect the corresponding metaphysical value.  As with the locus,
this information may be expressed in a number of ways.
.end


For example, suppose we we want to compile code to pick up an object
which we know will be sitting upright on the table.  This might be
reflected by the assertion
.unfill
	ASSERT FACT (obj ON_SURFACE TABLE upright)
.refill
where "obj" is a variable giving the location of the object, and 
"upright" has been defined somewhere as, say, [(0,0,0):(0,0,0)].
This piece of semantic information would be translated by the
system into assertions about the locus of obj:
.unfill
	ASSERT FACT (LOCUS obj ([(x,y,0)|(0,0,theta)]*upright) )

	ASSERT x %7≥%* 0; ASSERT x≤10; α{suppose table is 10 by 10α}
	ASSERT y %7≥%* 0; ASSERT y≤10;
.refill
The system can handle linear constraints on degrees of
freedom (in this case, x, y, and theta) within locus expressions.
For instance, suppose that we also know that the object is somewhere
on a strip running diagonally across the table.  This might
translate into
.unfill
	ASSERT x+y≤15;
	ASSERT x+y%7≥%*7;

.bull
so that the object locus is now given by

	[(x,y,0)|(0,0,theta)]*upright		{lc1: neweq}
	0≤x≤10
	0≤y≤10
	7≤x+y≤15
	-π≤theta≤π
.refill
Suppose that we now call a vision routine to locate the
object to within one centimeter and three degrees.  The vision
routine will store some value, say 
.unfill
	α[(3,7,0):(0,0,2.5)α] 
.refill
into the value cell for obj.  We clearly cannot know in
advance that this will be that value returned, so the locus estimate
given by {eqref lc1} will remain unchanged.
On the other hand, the determination
of obj has been improved to the point where the object can be picked
up.  In other words, if we execute the statement
.unfill
	MOVE YELLOW TO obj*objgrasp
.refill 
then we know that the yellow arm will wind up pretty close to
its nominal relative position for grasping the object.
In planning a trajectory to do this, the system will use its
nominal value for obj, which (in the absence of any better advice)
will be chosen at the center of the locus,
.unfill
	α[(5,5,0):(0,0,0)α]
.refill
and then modified at runtime in the usual way.

This trajectory modification may
cause problems if the runtime value of obj gets too far from the
nominal value. To avoid this, the expander will ask the trajectory
calculator to evaluate the suitability of its trajectory for extreme
points of the locus of obj.  If the modification seems to be too
great, then the expander will ask for several trajectories to be
planned and will generate conditional tests to select the correct
one. We are currently investigating ways to facilitate this
communication between the expander and the trajectory calculator. 
One very simple, though painstaking, method is to simulate moves to
a number of spots.  A better way would be for the trajectory
calculator to generate constraints telling what regions a trajectory
is good for, but it is a bit too early yet to tell how feasible this
will be.  Similarly, we are investigating ways for using runtime
errors to determine when splitting of a region may be needed.

.obd: NEWSS OBJECT DESCRIPTION

This section is intended to  provide an overview of the
sorts of information  about objects that HAL uses and
of how this information is currently specified.  It is
not intended to be a complete list of %4all%* the assertions
currently used or as a user's manual for building object
descriptions.

Our primary interest so far has been to investigate ways to use
descriptive information about objects, rather than to provide an
extremely elegant input language for the descriptions. This has
led us to specify explicitly a number of things which are, in
principle, computable from a more general shape description. We
expect that the process of describing an object to HAL, which is
currently almost completely manual, will eventually become very
largely automated, with most of the information being either directly
available or computed from the output of computer-aided-design
programs. 

Currently, objects are described by assertions about their
"interesting" properties. These assertions follow a number of
conventions, so that the various high level primitives can use the
information, although a user can, of course manipulate it explicitly.
Shape is treated simply as another object attribute, and a several
different shape descriptions may be present for a given object.

.newsss ONE-PIECE OBJECTS

Objects are represented as tree-like structures; typically, the
"root" node contains information about the object as a whole, with
"leaf" nodes telling about interesting features. By convention, we
use a frame variable for the object name. (This variable is then
assumed to give the object location).
.unfill
.bull 
For example,
.begin
.narrow 8,8
FRAME valvebody,bore1,bore2,bore3;
FRAME upright,upside_down;
PLANE topsurface;

ASSERT FACT(TYPE valvebody OBJECT); 
ASSERT FACT(GEOMED valvebody "valve.b3d"); 

ASSERT FACT(SUBPART valvebody bore1); 
ASSERT FACT(SUBPART valvebody bore2);
ASSERT FACT(SUBPART valvebody bore3);
ASSERT FACT(SURFACE valvebody topsurface );
.maybreak

ASSERT FACT(STABLE_POSITION valvebody upright);
ASSERT FACT(STABLE_POSITION valvebody upside_down);
upright α← α[(0,0,0):(0,0,0)α];
upside_down α← α[(0,0,1.8):(0,0,0)α];

topsurface <= (valvebody*(0,0,1.8)) α\ (Z WRT valvebody);

ATTACH bore1 TO valvebody AT α[(-1,0,2)|(0,0,0)α] RIGIDLY;
ATTACH bore2 TO valvebody AT α[(1,0,2)|(0,0,0)α] RIGIDLY;
ATTACH bore3 TO valvebody AT α[(1,0,3)|(0,0,0)α] RIGIDLY;
.end
.refill
declares that "valvebody" is an object whose GEOMED description
is given by file "valve.b3d".  There are three interesting "subparts",
called "bore1", "bore2", and "bore3" and located at α[(α-1,0,2)|(0,0,0)α], 
α[(1,0,2)|(0,0,0)α], and α[(1,0,3)|(0,0,0)α], respectively.  Also, there
is a planar surface called "topsurface" located at (0,0,1.8)α\Z
in body coordinates.  The valvebody can sit on the table in two "stable
positions", upright and upside_down.  Then, the assertion 
.unfill
	ASSERT FACT(valvebody ON_SURFACE table upside_down)
.refill
tells the system that the location of the valve body will be given by
an expression of the form:
.unfill
	α[(x,y,0)|(0,0,theta)α]*upside_down
.refill
for some values of x,y, and theta.

The subparts, "bore1", "bore2", and "bore3" are further described by
assertions of the form:

.unfill
.begin
.narrow 8,8
ASSERT FACT(TYPE bore1 BORE);
ASSERT FACT(DIAMETER bore1 0.9);
ASSERT FACT(THREAD bore1 8 32);
ASSERT FACT(LENGTH bore1 0.30);
ASSERT FACT(TOP_END bore1 hole1);
ASSERT FACT(BOTTOM_END bore1 OPEN); 

ASSERT FACT(TYPE hole1 HOLE);  
ASSERT FACT(LIES_IN hole1 topsurface); 
ASSERT FACT(CHAMFER_DEPTH hole1 0);
ASSERT FACT(CHAMFER_WIDTH hole1 0);
ASSERT FACT(LIP_SIZE hole1 (3/16));

      α{ et cetera α}
.end
.refill
Here the system recognizes the word "BORE" as saying that bore1 is
a negative cylinder (such as might result from a drilling operation).
The attributes  DIAMETER, THREAD, and LENGTH are obvious.  TOP_END
and BOTTOM_END, however, may require a bit more explanation.
The "top end" of a bore is always a hole -- ie, an intersection
between the bore and the object surface.  If the bore completely pierces
the object, then the bottom end will be also be a hole.  Otherwise,
it may be "OPEN"  (which means that it opens into some uninteresting 
cavity inside the object, "CLOSED" (which means that it comes to
an abrupt, but otherwise uninteresting, end), or a named surface
(which usually only happens for relatively large holes). 
see {newfig Bores and Holes,full} 

Frequently, a user wishes to declare a number of instances of a single
prototype.  This may be done by making assertions of the form.

.unfill
	ASSERT FACT(TYPE <object> <prototype>);
.bull
For instance, 

	ASSERT FACT(TYPE s1 screwtype1);
	ASSERT FACT(TYPE s2 screwtype1);
.REFILL
would declare s1 and s2 to be instances of screwtype1, where screwtype1
might be specified by
.unfill
	ASSERT FACT(TYPE screwtype1 SHAFT);
	ASSERT FACT(DIAMETER screwtype1 0.62);
	ASSERT FACT(LENGTH screwtype1 2.44);
	ASSERT FACT(THREAD screwtype1 3 16);
	ASSERT FACT(TOP_END screwtype1 headtype1); 
	ASSERT FACT(BOTTOM_END screwtype1 tiptype1);

	ASSERT FACT(TYPE headtype1 CYL_HEAD);
	ASSERT FACT(SLOT headtype1 HEX 0.53);

	ASSERT FACT(TYPE tiptype1 FLAT_END);
.refill
Note that shafts also are considered to be directed and to have two
ends.  By convention, all screws, bolts, or similar objects are
assumed to have their heads at the "top" end.  
see {newfig Shafts,full} 

.newsss ASSEMBLIES

An "assembly" is an object whose various subparts are removable.


For instance,
.unfill
	ASSERT FACT(TYPE waterpump ASSEMBLY);
	ASSERT FACT(SUBPART waterpump gasket);
	ASSERT FACT(SUBPART waterpump head);
	ASSERT FACT(SUBPART waterpump pumpbase);
	 :
	ASSERT FACT(gasket FITS ONTO waterpump AT α[(0,0,3):(0,0,0)α])
	 :
.refill
Such objects provide a convenient framework for assembly tasks.  
Typically, one of the subparts is chosen as a "base part", which
is used as an anchor
to which the remaining parts are added.

In addition to the usual sorts of object attributes and the
locations of the various subparts, assemblies usually contain
a number of "semantic" assertions about how things go together.
Some of this information is inherent in the design.  For instance,
.unfill
	ASSERT FACT(DESIGNED_TORQUE screw1 40)
.refill
Other information comes from the geometry of the parts, and (as 
indicated earlier) could theoretically be computed from the 
shape description but is of
enough interest to be worth representing directly, especially
in cases where the computation required is non-trivial.
For example,
.unfill
	 :
	ASSERT FACT(MATED pumpbase.top_surf gasket.bottom_surf)
	ASSERT FACT(ALIGNED head.bore1 gasket.bore1 pumpbase.bore1)
	ASSERT FACT(RUNS_THRU s1 gasket.bore1;
	ASSERT FACT(RUNS_THRU s1 head.bore1);
	ASSERT FACT(SCREWED_INTO s1 pumpbase.bore1);
	 :
.refill
.NEWSS EXAMPLE: WATERPUMP ASSEMBLY PROGRAM,WATERPUMP ASSEMBLY

This short example is intended to give some feel for what a very high
level program for a simple task looks like.

The task here is to mate the pump head and gasket with the
pump base using two aligning pins, then to secure the head with six
machine screws.  This task requires only a few basic operations, the
principle ones being FIT ... ONTO and INSERT, and is very similar to
one actually performed by WAVE at Stanford.  The WAVE program for
this task consists of about 450 lines of "machine language"-like
code, and was written over a period of several weeks by Bob Bolles and
Lou Paul. (Most of this time was spent on improving WAVE and
developing techniques; more recent tasks of similar complexity
have taken on the order of three to eight days)
The same program, rewritten in low-level HAL, would
be somewhat shorter, although it would still require a fair amount
of detailed effort on the part of the programmer.  
.unfill
BEGIN "PUMP"

α{ Note that the characters "α{ α}" are comment delimiters α}

REQUIRE "PUMP.075" SOURCE_FILE; 

.COMT 4
      α{ This file would contain many assertions describing the
	pump assembly and all its subparts.  Eventually,
	such descriptions will most likely be produced
	as part of the output of design automation programs.
	See the section on object descriptions for a sampling
	of the sorts of things one might see here α}
.END


REQUIRE "STATN.04" SOURCE_FILE;

.COMT 4
      α{ Reads in description of the work station. This would
	include location of tool racks, standard "jigs" that
	may be available, etc. α}
.END
.maybreak

ASSERT FACT( pumpbase ON_SURFACE conveyor_belt IN POSITION upright);

.COMT 4
      α{ This assertion tells the strategist the initial 
	location & "stable position" (ie "upright") of the pumpbase.
	The meaning of "upright" is given by an assertion of
	the form:
	
	ASSERT FACT (STABLE_POSITION pumpbase upright α[(0,0,0):(0,0,0)α]);

	which is assumed to be part of the file "PUMP.075".

	The dynamic frame "conveyor_belt" is assumed to have been
	defined in "STATN.04".  Actually, such moving devices 
	won't be handled by early versions of HAL.  An alternative
	would be to arrange the pumpbases in an array to
	one side of the work station (perhaps using an "egg carton"
	arrangement to make it easier to pick one out).  α}
.END
.maybreak

ASSERT FACT( pumphead ON_SURFACE side_table IN POSITION onside);

      α{  < a number of other assertions might go here> α}
	
.maybreak
.COMT 4
      α{ Here, we will use TASK BEGINs and allow the system to
	decide on the relative order of the various subtasks. α}
.END

TASK BEGIN "aligning pins"

	INSERT pin1 INTO pumpbase.hole1;
	INSERT pin1 INTO pumpbase.hole2;

     END;

.COMT 12
      α{ Note here that we are allowing the system to decide
	how it will locate the pumpbase and whether it
	will leave it on the conveyor belt throughout the
	assembly or place it in some temporary work area.
	Of course, we could have made these decisions explicitly.
	For instance, 
		
.END
.begin
.narrow 4,0
		PLACE pumpbase ON TABLE 
			IN POSITION upright
			WITH ALIGNMENT left_side AGAINST wall1,
				       back_end AGAINST wall2;

.end
.COMT 12
	could have been the first statement of the program.
.maybreak

	"wall1" & "wall2" are low walls described in
	"STATN.04" and form a corner which could be used as 
	a simple jig.  "left_side" & "back_end" here would be 
	defined in "PUMP.075"  as components in the "footprint"
	of the pumpbase.  (see the section on object description
	for further details) α}
.END

.maybreak
FIT gasket ONTO pumpbase_assembly 
	WITH ALIGNMENT gasket.hole1 OVER pin1,
		       gasket.hole2 OVER pin2;

.COMT 12
      α{ The system uses its object model for the
	pumpbase assembly to tell it how the gasket
	fits onto the pumpbase α}
.END
.maybreak

FIT pumphead ONTO pumpbase_assembly
	WITH ALIGNMENT head.hole1 OVER pin1,
		       head.hole2 OVER pin2;
.maybreak

TASK BEGIN "boltdown"

	BEGIN "s3op"
	INSERT s3 INTO head.hole3
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;
.maybreak

	BEGIN "s4op"
	INSERT s4 INTO head.hole4
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;

.maybreak
	BEGIN "s5op"
	INSERT s5 INTO head.hole5
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;
.maybreak

	BEGIN "s6op"
	INSERT s6 INTO head.hole6
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;
.maybreak

	BEGIN "pull_pin1"
	PREREQUISITE "s3op";
	INSERT pin1 INTO rack.hole1;
.COMT 12
	      α{ Once we have s3 in h3, then we can remove
		pin1 without fear of letting the
		head slip out of alignment, since pin2
		will stay until there is a screw in
		hole 4.α}
.END
	END;
.maybreak

	BEGIN "pull_pin2"
	PREREQUISITE "s4op";
	INSERT pin2 INTO rack.hole2;
	END;

.maybreak
	BEGIN "s1op"
	PREREQUISITE "pull_pin1";
	INSERT s1 INTO head.hole1
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;
.maybreak

	BEGIN "s2op"
	PREREQUISITE "pull_pin1";
	INSERT s2 INTO head.hole2
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;

     END;

.maybreak
TASK BEGIN "torque head"

	COMPILE FOREACH FACT( SUBPART pump_assembly BIND(s) )
		COMPILE IF FACT(TYPE α#(s) screwtype1)
		     ∧ FACT(α#(s) designed_torque BIND(t))) THEN
			TIGHTEN α#(s) WITH TORQUE α#(t)
				WITH TOOL driver1;

     END;

.COMT 12
      α{ This will tighten all subparts of the pump assembly
	which are of type "screwtype1" and have a specified design
	torque.   It might expand into something like:
.END

.begin
.narrow 8,0
	TASK BEGIN "torque head"
		TIGHTEN s1 WITH TORQUE t1 WITH TOOL driver1;
		TIGHTEN s2 WITH TORQUE t2 WITH TOOL driver1;
		:
		TIGHTEN s6 WITH TORQUE t6 WITH TOOL driver1;
		END;   
	α}
.end	

.maybreak
PLACE pump_assembly ON conveyor_belt IN POSITION upright;

.COMT 4
      α{ This will cause the system to pick an orientation
	for the completed pump assembly & put it on the conveyor.
	The system can, of course, "remember" the position
	it picks.  If there were some further task to
	be done on the pump, the system will know where
	to find it.  α}

.END
END;

.refill
.MAKEFIG(Pump Assembly Station,full)
.next page
.allpic